﻿using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Kikooland.Core;
using System;
using System.Collections.Generic;

namespace Kikooland
{
    /// <summary>
    /// Représente le joueur
    /// </summary>
    public class Player : Entity
    {
        public int Niveau { get; set; }
        public int PointsDeVie { get; set; }
        public int Puissance { get; set; }
        public int Vivacite { get; set; }
        public int Sagacite { get; set; }

        /// <summary>
        /// Mob ciblé. 1 seul au maximum pour le moment
        /// </summary>
        public Mob TargetedMob { get; set; }

        /// <summary>
        /// Les actions possibles
        /// </summary>
        public Actions Actions { get; set; }

        private List<Mob> Mobs
        {
            get
            {
                return World.GetInstance().Mobs;
            }
        }

        public Player()
            : base()
        {
            Actions = new Actions();
        }

        #region Public methods

        public Spell GetPointedSpell(MouseState mouse)
        {
            foreach (var spell in Actions.Spells)
            {
                if (Rectangle.Intersect(MouseStateEx.GetRectangle(mouse), spell.ActionBarPosition.ToRectangle()).Width > 0)
                    return spell;
            }
            return null;
        }

        public override void Update(GameTime gameTime, MouseState mouseState, KeyboardState keyboardState)
        {
            // Gestion du déplacement
            UpdatePosition(keyboardState);

            // Gestion du mob ciblé
            Mob pointedMob = World.GetInstance().GetPointedMob(mouseState);

            // Gestion du sort survolé
            Spell pointedSpell = this.GetPointedSpell(mouseState);

            // Pour le moment, seul le premier sort est dispo
            Spell sortDispo = this.Actions.Spells[0];

            // TODO :
            //  -> gérer le CD quand état Casted (pas de CD pour le moment)

            // Possibilités selon l'état du SpellCast
            switch (sortDispo.SpellCastState)
            {
                case SpellCastState.UnCastable:
                    ManageUnCastableState(sortDispo);
                    break;
                case SpellCastState.Castable:
                    ManageCastableState(ref mouseState, ref keyboardState, sortDispo);
                    break;
                case SpellCastState.Casting:
                    ManageCastingState(gameTime, sortDispo);
                    break;
                case SpellCastState.Casted:
                    ManageCastedState(pointedMob, sortDispo, gameTime);
                    break;
                default:
                    break;
            }

            // Gestion de la sélection de la cible
            ManageTargetSelection(ref mouseState, ref keyboardState, pointedMob, sortDispo);
        }

        public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            base.Draw(gameTime, spriteBatch);
        }

        #endregion

        #region Private methods

        private void UpdatePosition(KeyboardState keyboardState)
        {
            if (keyboardState.GetPressedKeys().Count() > 0)
            {
                var diff = new Point();
                if (keyboardState.GetPressedKeys()[0] == Keys.Up)
                    diff.Y = -5;
                else if (keyboardState.GetPressedKeys()[0] == Keys.Down)
                    diff.Y = 5;
                else if (keyboardState.GetPressedKeys()[0] == Keys.Left)
                    diff.X = -5;
                else if (keyboardState.GetPressedKeys()[0] == Keys.Right)
                    diff.X = 5;
                this.Position = new RectangleEx(this.Position.X + diff.X, this.Position.Y + diff.Y, 50, 80);
            }
        }

        private void ManageTargetSelection(ref MouseState mouseState, ref KeyboardState keyboardState, Mob pointedMob, Spell sortDispo)
        {
            // Gestion de sélection / déselection de cible
            if (this.TargetedMob != null &&
                (mouseState.LeftButton == ButtonState.Pressed &&
                pointedMob == null &&
                this.GetPointedSpell(mouseState) == null) ||
                (keyboardState.GetPressedKeys().Count() > 0 && keyboardState.GetPressedKeys()[0] == Keys.Escape))
            {
                this.TargetedMob = null;
                if (sortDispo.SpellCastState != SpellCastState.Casted)
                    sortDispo.SpellCastState = SpellCastState.UnCastable;
            }
            else if (this.TargetedMob == null &&
                     mouseState.LeftButton == ButtonState.Pressed &&
                     pointedMob != null &&
                     this.GetPointedSpell(mouseState) == null)
            {
                this.TargetedMob = pointedMob;
                sortDispo.SpellCastState = SpellCastState.Castable;
            }
        }

        private void ManageCastedState(Mob pointedMob, Spell sortDispo, GameTime gameTime)
        {
            // Quand Casted, le projectile se dirige vers la cible. MAJ de la position du projectile
            foreach (var item in sortDispo.SpellCastResult)
            {
                if (!item.TargetReached)
                {
                    item.Update(gameTime);

                    // Détermination de la collision
                    if (Rectangle.Intersect(item.Position.ToRectangle(), item.Target.Position.ToRectangle()).Width > 0)
                    {
                        // Perte de LP du mob
                        sortDispo.TargetedMob.LifePoints -= 5;
                        item.TargetReached = true;
                    }
                }
            }

            // Pas de modification de l'état du sort tant qu'un projectile n'a pas atteint sa cible
            if (sortDispo.SpellCastResult.Where(x => x.TargetReached).Count() == sortDispo.SpellCastResult.Count)
            {
                // Nouvel état selon sélection en cours
                sortDispo.SpellCastState = (pointedMob != null) ? SpellCastState.Castable : SpellCastState.UnCastable;
                foreach (var item in sortDispo.SpellCastResult)
                {
                    item.TargetReached = false;
                }
            }

            // Gestion de la cible tuée
            if (sortDispo.TargetedMob.LifePoints <= 0)
            {
                sortDispo.SpellCastState = SpellCastState.UnCastable;
                this.Mobs.Remove(sortDispo.TargetedMob);
                sortDispo.TargetedMob = null;
                this.TargetedMob = null;
                foreach (var item in sortDispo.SpellCastResult)
                    item.TargetReached = false;
            }            
        }

        private void ManageCastingState(GameTime gameTime, Spell sortDispo)
        {
            // En cours de cast :
            //   -> incrémenter le cast timer
            //   -> si le cast timer atteint le max, on passe à Casted
            //   -> si non, on vérifie le déciblage. Si déciblé, on repasse à UnCastable
            sortDispo.SpellCastCurrentTimer += gameTime.ElapsedGameTime.Milliseconds;
            if (sortDispo.SpellCastCurrentTimer >= sortDispo.SpellCastMaxTimer)
            {
                sortDispo.SpellCastState = SpellCastState.Casted;
                sortDispo.SpellCastCurrentTimer = 0;
                sortDispo.TargetedMob = this.TargetedMob;

                sortDispo.SpellCastResult[0].Target = this.Mobs[0];
                sortDispo.SpellCastResult[1].Target = this.Mobs[0];
                sortDispo.SpellCastResult[2].Target = this.Mobs[0];

                sortDispo.SpellCastResult[3].Target = this.Mobs[0];
                sortDispo.SpellCastResult[4].Target = this.Mobs[0];
                sortDispo.SpellCastResult[5].Target = this.Mobs[0];

                foreach (var item in sortDispo.SpellCastResult)
                {
                    // Cible
                    //item.Target = this.TargetedMob;
                    // Départ
                    item.Position = new RectangleEx(this.Position.X + this.Position.Width + 10, this.Position.Y, 10, 10); 
                    // Equation du mouvement
                    item.SetLinearMovementEquation(
                        new Vector2(item.Position.X, item.Position.Y),
                        new Vector2(item.Target.Position.X, item.Target.Position.Y));
                }                
            }
            else
            {
                if (this.TargetedMob == null)
                {
                    sortDispo.SpellCastState = SpellCastState.UnCastable;
                    sortDispo.SpellCastCurrentTimer = 0;
                    sortDispo.SpellCastTimerBarPosition = new RectangleEx(200, 630, 50, 15);
                }
            }
        }

        private void ManageCastableState(ref MouseState mouseState, ref KeyboardState keyboardState, Spell sortDispo)
        {
            // Pour passer à Casting, il faut cliquer sur le bouton du sort
            if (this.GetPointedSpell(mouseState) != null && mouseState.LeftButton == ButtonState.Pressed ||
               (keyboardState.GetPressedKeys().Count() > 0 && keyboardState.GetPressedKeys()[0] == Keys.NumPad1))
                sortDispo.SpellCastState = SpellCastState.Casting;
        }

        private void ManageUnCastableState(Spell sortDispo)
        {
            // Pour passer à Castable, il faut une cible
            if (this.TargetedMob != null)
                sortDispo.SpellCastState = SpellCastState.Castable;
        }

        #endregion
    }

    public enum PlayerState
    {
        Moving,
        Standing
    }

    public sealed class MouseStateEx
    {
        public static Rectangle GetRectangle(MouseState mouseState)
        {
            return new Rectangle(mouseState.X, mouseState.Y, 1, 1);
        }
    }
}
